#include "util.h"

void printPageLayout(page_t *page)
{
    if (page == NULL || page->mem == NULL) {
        printf("NULL PAGE\n");
    } else {
        printf("Page Layout:\n");
        printf("memory:     %p\n", page->mem);
        printf("used flag:  %p\toffset: %ld\tsize: %ld \tvalue: %d\n",  page->is_used,
                                                                        (char *) page->is_used - (char *) page->mem, 
                                                                        (char *) page->slot - (char *) page->is_used,
                                                                        PAGE_GET(page->is_used)); 
        printf("type flag:  %p\toffset: %ld\tsize: %ld \tvalue: %d\n",  page->type,
                                                                        (char *) page->type - (char *) page->mem, 
                                                                        (char *) page->slot - (char *) page->type,
                                                                        PAGE_GET(page->type)); 
        printf("slot:       %p\toffset: %ld\tsize: %ld \tvalue: %ld\n", page->slot, 
                                                                        (char *) page->slot - (char *) page->mem, 
                                                                        (char *) page->lower - (char *) page->slot, 
                                                                        PAGE_RECORD_NUM(page));
        printf("lower:      %p\toffset: %ld\tsize:    \n", page->lower,
                                                           (char *) page->lower - (char *) page->mem); 
        printf("upper:      %p\toffset: %ld\tsize:    \n", page->upper,
                                                           (char *) page->upper - (char *) page->mem); 
        printf("free mem:   %p\toffset: %ld\tsize: %ld\n", page->end,
                                                           (char *) page->lower - (char *) page->mem, 
                                                           (char *) page->upper - (char *) page->lower); 
        printf("end:        %p\toffset: %ld\tsize: %ld\n", page->end, 
                                                           (char *) page->end - (char *) page->mem,
                                                           (char *) page->end - (char *) page->upper); 
    }
}

void printRecord(page_t *page, int type)
{
    switch (type) {
        case BORROW:
            // printf("(%s, %s, %s, %s, %s, %s)\n", "borrow_id", "book_id", "user_id",
            //     "borrow_date", "return_date", "status");
            for (int i = 0; i < PAGE_RECORD_NUM(page); i++) {
                int off = (page->slot + i)->off;
                char *start = (char *) page->end - off;

                record_t record;
                record.table = BORROW;
                deserialize(start, &record);

                printOneRecord(&record, record.table);
            }
            break;

        case BOOK:
            // printf("(%s, %s, %s, %s, %s, %s)\n", "book_id", "quantity", "available",
                // "category", "title", "author");
            for (int i = 0; i < PAGE_RECORD_NUM(page); i++) {
                int off = (page->slot + i)->off;
                char *start = (char *) page->end - off;

                record_t record;
                record.table = BOOK;
                deserialize(start, &record);

                printOneRecord(&record, record.table);
            }
            break;

        case USER:
            // printf("(%s, %s, %s)\n", "user_id", "name", "email");
            for (int i = 0; i < PAGE_RECORD_NUM(page); i++) {
                int off = (page->slot + i)->off;
                char *start = (char *) page->end - off;

                record_t record;
                record.table = USER;
                deserialize(start, &record);

                printOneRecord(&record, record.table);
            }
            break;

        default:
            return;
    }
}

void printSlot(page_t *page)
{
    for (int i = 0; i < PAGE_RECORD_NUM(page); i++) {
        slot_t *slot = page->slot + i;
        printf("Slot%d ---> id: %d\tindex: %d\toff: %d  \tlen: %d\n",
                i, slot->slot_id, slot->index, slot->off, slot->len);
    }
}

int printTable(table_t *table)
{
    printf("Table Layout:\n");
    printf("type:           %d\n", table->type);
    printf("record count:   %d\n", table->record_count);

    Position node = table->tree;
    page_t *page;

    while (node != NULL) {
        if (node->Children[0] != NULL)
            node = node->Children[0];
        else 
            break;
    }

    int actual_count = 0;
    while (node != NULL) {
        page = &(node->page);
        // printSlot(page);
        printRecord(page, table->type);
        actual_count += PAGE_RECORD_NUM(page);
        node = node->Next;
    }
    printf("actual record count: %d\n", actual_count);

    if (actual_count != table->record_count) {
        // fprintf(stderr, "---------------------------TEST FAILURE-----------------------------\n");
        return -1;
    } else {
        // printf("---------------------------TEST SUCCESS-----------------------------\n");
        return 0;
    }
}

void generateRecord(int key, record_t *record, int type)
{
    switch (type) {
        case BOOK:
            (*record).table = BOOK;
            (*record).val_book.book_id = key;
            (*record).val_book.quantity = rand() % 500;
            (*record).val_book.available = rand() % 500;
            (*record).val_book.category = rand() % 3;
            (*record).index = (*record).val_book.book_id;
            snprintf((*record).val_book.title, MAX_TITLE_LEN, "Book%d", (*record).index);
            snprintf((*record).val_book.author, MAX_NAME_LEN, "Author%d", (*record).index);
            break;

        case BORROW:
            (*record).table = BORROW;
            (*record).val_borrow.borrow_id = key;
            (*record).val_borrow.book_id = rand() % 500;
            (*record).val_borrow.user_id = rand() % 500;
            (*record).val_borrow.status = rand() % 3;
            (*record).val_borrow.borrow_date.year = 2024;
            (*record).val_borrow.borrow_date.month = rand() % 12;
            (*record).val_borrow.borrow_date.day = rand() % 31;
            (*record).val_borrow.return_date.year = 2024;
            (*record).val_borrow.return_date.month = rand() % 12;
            (*record).val_borrow.return_date.day = rand() % 31;
            (*record).index = (*record).val_borrow.borrow_id;
            break;
            
        case USER:
            (*record).table = USER;
            (*record).index = key;
            (*record).val_user.user_id = key;
            snprintf((*record).val_user.name, MAX_NAME_LEN, "User%d", (*record).index);
            snprintf((*record).val_user.email, MAX_EMAIL_LEN, "user%d@qq.com", (*record).index);
            break;

        default:
            break;
    }
}

void printOneRecord(record_t *record, int type)
{
    switch (type) {
        case BORROW:
            printf("%d\t%d\t%d\t%d-%d-%d\t%d-%d-%d\t%d\n", record->val_borrow.borrow_id,
                    record->val_borrow.book_id, record->val_borrow.user_id,
                    record->val_borrow.borrow_date.year, record->val_borrow.borrow_date.month,
                    record->val_borrow.borrow_date.day, record->val_borrow.return_date.year,
                    record->val_borrow.return_date.month, record->val_borrow.return_date.day,
                    record->val_borrow.status);
            break;

        case BOOK:
            printf("%d\t%d\t%d\t%d\t%-10s%-10s\n", record->val_book.book_id,
                    record->val_book.quantity, record->val_book.available,
                    record->val_book.category, record->val_book.title,
                    record->val_book.author);
            break;

        case USER:
            printf("%d\t%-10s%-10s\n", record->val_user.user_id,
                    record->val_user.name, record->val_user.email);
            break;

        default:
            return;
    }

}